package com.binance.notification.web.service.impl;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import com.binance.notification.api.response.WebSDKInitParamsResponse;
import com.binance.notification.web.push.JClient;
import com.binance.notification.web.service.IDeviceService;
import com.binance.notification.web.utils.Constants;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import cn.jiguang.common.resp.APIConnectionException;
import cn.jiguang.common.resp.APIRequestException;
import cn.jpush.api.device.AliasDeviceListResult;
import cn.jpush.api.device.TagAliasResult;
import cn.jpush.api.device.TagListResult;
import io.jsonwebtoken.lang.Collections;
import lombok.extern.slf4j.Slf4j;


@Service
@Slf4j
public class DeviceServiceImpl implements IDeviceService {

    @Autowired
    private JClient jClient;

    @Value("${jpush.device.host.name}")
    private String deviceHostName;
    @Value("${jpush.enabled.languages:en}")
    private Set<String> languages;
    @Autowired
    private RestTemplate restTemplate;

    @Override
    public Set<String> languageTags() throws APIConnectionException, APIRequestException {
        if (this.languages != null && this.languages.size() > 1) {
            return this.languages;
        }
        TagListResult result = jClient.getDeviceClient().getLanguages();
        if (result.isResultOK()) {
            if (result.tags != null) {
                // 此处主要是未了绕开测试阶段时注册的zh语言，没有其他用途
                result.tags.remove("zh");
                return Sets.newHashSet(result.tags.iterator());
            }
        } else {
            log.error("查询标签出现异常：{}", result.getOriginalContent());
        }
        return Sets.newHashSet();
    }

    @Override
    public Map<String, Object> aliasTags(String registrationId) throws APIConnectionException, APIRequestException {
        Map<String, Object> map = Maps.newHashMap();
        TagAliasResult result = this.jClient.getDeviceClient().getDeviceTagAlias(registrationId);
        if (result.isResultOK()) {
            if (result.tags != null) {
                map.put("tags", result.tags);
            }
            if (result.alias != null) {
                map.put("alias", result.alias);
            }
        } else {
            log.error("查询标签出现异常：{}", result.getOriginalContent());
        }
        return map;
    }

    @Override
    public Map<String, Object> setAliasAndTags(String registrationId, String alias, List<String> tagsToAdd,
            List<String> tagsToRemove) throws APIConnectionException, APIRequestException {
        if (StringUtils.isBlank(alias) && Collections.isEmpty(tagsToAdd) && Collections.isEmpty(tagsToRemove)) {
            throw new IllegalArgumentException("alias（别名）、tagsToAdd（要添加的标签）、tagsToRemove（要删除的标签）不能都为空！");
        }
        if (tagsToAdd == null) {
            tagsToAdd = Lists.newArrayList();
        }
        if (tagsToRemove == null) {
            tagsToRemove = Lists.newArrayList();
        }
        Set<String> tagsToAddSet = Sets.newLinkedHashSet(tagsToAdd);
        Set<String> tagsToRemoveSet = Sets.newLinkedHashSet(tagsToRemove);
        Set<String> languagesForAdd = Sets.filter(tagsToAddSet, tag -> {
            if (StringUtils.startsWith(tag, Constants.LANGUAGE_TAG_PRIX)) {
                return true;
            }
            return false;
        });
        if (languagesForAdd.size() > 1) {
            // 如果要设置的语言标签有多个，则只取第一个，即删除其他的
            Set<String> removed = Sets.newHashSet();
            Iterator<String> iter = languagesForAdd.iterator();
            for (int i = 0; iter.hasNext(); i++) {
                String tag = iter.next();
                if (i > 0) {
                    removed.add(tag);
                }
            }
            languagesForAdd.removeAll(removed);
        }

        Map<String, Object> aliasTags = null;
        // 如果经过计算后，还有要设置的语言标签
        if (languagesForAdd.size() == 1) {
            String language = languagesForAdd.iterator().next();
            // 要设置的这个语言标签是不存在要删除的标签中
            if (!tagsToRemoveSet.contains(language)) {
                aliasTags = this.aliasTags(registrationId);
                @SuppressWarnings("unchecked")
                List<String> tags = (List<String>) aliasTags.get("tags");
                if (tags != null) {
                    // 要设置的语言已经存在
                    if (tags.contains(language)) {
                        // 将要设置的语言标签从要添加标签集合中删除
                        languagesForAdd.clear();
                    }
                    for (Iterator<String> iter = tags.iterator(); iter.hasNext();) {
                        String tag = iter.next();
                        if (tag.startsWith(Constants.LANGUAGE_TAG_PRIX) && !tag.equals(language)) {
                            tagsToRemoveSet.add(tag);
                            iter.remove();
                        }
                    }
                }
            }
        }
        this.jClient.getDeviceClient().updateDeviceTagAlias(registrationId, StringUtils.defaultIfBlank(alias, ""),
                tagsToAddSet, tagsToRemoveSet);
        if (aliasTags != null) {
            @SuppressWarnings("unchecked")
            List<String> tags = (List<String>) aliasTags.get("tags");
            if (tags == null) {
                tags = Lists.newArrayList();
            }
            tags.addAll(tagsToAddSet);
            tags.removeAll(tagsToRemoveSet);

            aliasTags.put("tags", tags);
            aliasTags.put("alias", StringUtils.defaultIfBlank(alias, ""));
        } else {
            aliasTags = this.aliasTags(registrationId);
        }
        return aliasTags;
    }

    @Override
    public WebSDKInitParamsResponse getWebSDKInitParams(String userStr) {
        Map<String, String> map = this.jClient.getWebSDKInitParams(userStr);
        WebSDKInitParamsResponse au = new WebSDKInitParamsResponse();
        au.setUserStr(map.get("userStr"));
        au.setAppkey(map.get("appkey"));
        au.setRandom_str(map.get("randomStr"));
        au.setSignature(map.get("signature"));
        au.setTimestamp(map.get("timestamp"));
        au.setWsAddress(map.get("wsAddress"));
        au.setGlobalTags(map.get("globalTags"));
        au.setLanguagePrefix(map.get("languagePrefix"));
        au.setDebug(Boolean.parseBoolean(map.get("debug")));
        log.info("{}", au);
        return au;
    }

    @Override
    public void setLanguage(String registrationId, String language) throws APIConnectionException, APIRequestException {
        Map<String, Object> map = this.aliasTags(registrationId);
        boolean hasGlobalTag = false;
        @SuppressWarnings("unchecked")
        List<String> tags = (List<String>) map.get("tags");
        Set<String> tagsToAdds = Sets.newHashSet();
        Set<String> tagsToRemoves = Sets.newHashSet();
        if (tags != null) {
            for (String tag : tags) {
                if (StringUtils.startsWith(tag, Constants.LANGUAGE_TAG_PRIX)) {
                    tagsToRemoves.add(tag);
                }
                if (StringUtils.equals(tag, Constants.GLOBAL_TAG)) {
                    hasGlobalTag = true;
                }
            }
        }
        if (!hasGlobalTag) {
            tagsToAdds.add(Constants.GLOBAL_TAG);
        }
        if (!StringUtils.startsWith(language, Constants.LANGUAGE_TAG_PRIX)) {
            language = Constants.LANGUAGE_TAG_PRIX + language;
        }
        // 本来就不存在要设置的语言
        if (!tagsToRemoves.remove(language)) {
            tagsToAdds.add(language);
        }
        if (tagsToAdds.isEmpty() && tagsToRemoves.isEmpty()) {
            return;
        }
        this.setAliasAndTags(registrationId, (String) map.get("alias"), Lists.newArrayList(tagsToAdds),
                Lists.newArrayList(tagsToRemoves));
    }

    @Override
    public List<String> getAliasDeviceList(String alias, String platform)
            throws APIConnectionException, APIRequestException {
        AliasDeviceListResult result = this.jClient.getDeviceClient().getAliasDeviceList(alias, platform);
        return result.registration_ids;
    }

    @Override
    public Long countUidsByTagAndPlatform(String tag, String platform) {
        String url = this.deviceHostName + "/v3/uids/tag/" + tag + "/platform/" + this.getPlatformChar(platform);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.add(org.apache.http.HttpHeaders.AUTHORIZATION, this.jClient.getBasicAuthorization());
        ResponseEntity<String> responseEntity =
                restTemplate.exchange(url, HttpMethod.GET, new HttpEntity<byte[]>(headers), String.class);
        if (responseEntity.getStatusCodeValue() != 200) {
            throw new RuntimeException(responseEntity.getBody());
        }
        return Long.parseLong(responseEntity.getBody());
    }

    @Override
    public List<String> getUidListByAliasAndPlatform(String alias, String platform) {
        String url = this.deviceHostName + "/v3/uids/alias/" + alias + "/platform/" + this.getPlatformChar(platform);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.add(org.apache.http.HttpHeaders.AUTHORIZATION, this.jClient.getBasicAuthorization());
        ResponseEntity<String> responseEntity =
                restTemplate.exchange(url, HttpMethod.GET, new HttpEntity<byte[]>(headers), String.class);
        if (responseEntity.getStatusCodeValue() != 200) {
            throw new RuntimeException(responseEntity.getBody());
        }
        String uids = responseEntity.getBody();
        String[] array = StringUtils.split(uids, ",");
        if (array == null || array.length == 0) {
            return Lists.newArrayList();
        }
        return Arrays.asList(array);
    }

    private String getPlatformChar(String platform) {
        switch (platform.toLowerCase()) {
            case "android":
            case "a":
                return "a";
            case "ios":
            case "i":
                return "i";
            case "web":
            case "b":
                return "b";
            case "windows":
            case "pc":
            case "c":
                return "c";
            default:
                return null;
        }
    }
}
